package com.dream.myqiyi.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import android.util.Log;

import com.dream.myqiyi.Global;
import com.dream.myqiyi.dao.NoteModel;
import com.dream.myqiyi.dao.NoteResourceModel;
import com.dream.myqiyi.dao.NoteTagsModel;
import com.dream.myqiyi.dao.NotebookModel;
import com.dream.myqiyi.dao.TagModel;
import com.dream.myqiyi.util.StringUtils;

public class NoteService {

	Map<String, NoteModel>			allNotes;
	boolean							isDirty;
	List<NoteModel>					masterNoteIndex;
	List<NoteModel>					noteIndex;
	NoteModel						currNoteModel = null;
	NotebookModel					currNotebookModel = null;
	TagModel						currTagModel = null;
	String							currNoteGuid = "";
	NoteManager						noteMgr = null;
	
	public NoteService() {
		allNotes = new HashMap<String, NoteModel>();
		noteMgr = (NoteManager)ApplicationContext.getSingleton().getBean("NoteManager");
		isDirty = true;
	}
	
	public List<NoteModel> getMasterNoteIndex() {
		return masterNoteIndex;
	}
	
	public List<NoteModel> getNoteIndex() {
		return masterNoteIndex;
	}
	
	public NoteModel getCurrNoteModel() {
		return currNoteModel;
	}
	
	public void setCurrNoteModel(NoteModel note) {
		currNoteModel = note;
	}
		
	public ArrayList<NoteModel> getNotes() {
		if (isDirty) {
			updateAllNotes();
		}
		return new ArrayList<NoteModel>(allNotes.values());
	}
	
	public ArrayList<NoteModel> getActiveNotes() {
		if (isDirty) {
			updateAllNotes();
		}
		ArrayList<NoteModel> ret = new ArrayList<NoteModel>();
		for (Map.Entry<String, NoteModel> entry : allNotes.entrySet()) {
			NoteModel note = entry.getValue();
			if (note.isActive()) {
				ret.add(note);
			}
		}
		return ret;
	}
	
	public List<NoteModel> getNotebookNotes(String notebookGuid) {
		if (isDirty) {
			updateAllNotes();
		}
		List<NoteModel> ret = new ArrayList<NoteModel>();
		for (Map.Entry<String, NoteModel> entry : allNotes.entrySet()) {
			NoteModel note = entry.getValue();
			if (notebookGuid.equals(note.getNotebookGuid())) {
				ret.add(note);
			}
		}
		return ret;
	}
	
	public void saveNote(NoteModel note) {
    	Calendar currentTime = new GregorianCalendar();
    	if (note.getContent().indexOf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") == -1) {
         	StringBuffer noteString = new StringBuffer(100);
         	noteString.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
         		"<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">\n" +
         		"<en-note>\n");
         	noteString.append(note.getContent());
         	noteString.append("</en-note>");
         	note.setContent(fromDispHtml(noteString.toString()));
    	}
    	if (StringUtils.isNull(note.getNotebookGuid())) {
        	NotebookService nbSrv = (NotebookService)ApplicationContext.getSingleton().getBean("NotebookService");
    		note.setNotebookGuid(nbSrv.getCurrNotebook().getGuid());
    	}

    	long currTime = System.currentTimeMillis();
    	if (note.getCreated() == 0) {
    		note.setCreated(currTime);
    	}
    	note.setUpdated(currTime);

    	note.setUpdateSequenceNumber(note.getUpdateSequenceNumber() + 1);
    	note.setAttributeAuthor(Global.username);
    	
		noteMgr.save(note);
		note.setContent(toDispHtml(note.getContent()));
		allNotes.put(note.getGuid(), note);
		//isDirty = true;
	}
	
	public List<NoteModel> getNotebookActiveNotes(String notebookGuid) {
		if (isDirty) {
			updateAllNotes();
		}
		List<NoteModel> ret = new ArrayList<NoteModel>();
		for (Map.Entry<String, NoteModel> entry : allNotes.entrySet()) {
			NoteModel note = entry.getValue();
			if (notebookGuid.equals(note.getNotebookGuid()) && note.isActive()) {
				ret.add(note);
			}
		}
		return ret;
	}
	
	public List<NoteModel> getTagNotes(String tagGuid) {
		if (isDirty) {
			updateAllNotes();
		}
		
		NoteTagsManager noteTagMgr = (NoteTagsManager)ApplicationContext.getSingleton().getBean("NoteTagsManager");
		List<NoteTagsModel> ntList = noteTagMgr.findByTagGuid(tagGuid);
		List<NoteModel> ret = new ArrayList<NoteModel>();
		for (NoteTagsModel nt : ntList) {
			NoteModel note = getNote(nt.getNoteGuid());
			if (note != null) {
				ret.add(note);
			}
		}
		return ret;
	}

	public List<NoteModel> getTagActiveNotes(String tagGuid) {
		if (isDirty) {
			updateAllNotes();
		}
		
		NoteTagsManager noteTagMgr = (NoteTagsManager)ApplicationContext.getSingleton().getBean("NoteTagsManager");
		List<NoteTagsModel> ntList = noteTagMgr.findByTagGuid(tagGuid);
		List<NoteModel> ret = new ArrayList<NoteModel>();
		for (NoteTagsModel nt : ntList) {
			NoteModel note = getNote(nt.getNoteGuid());
			if (note != null && note.isActive()) {
				ret.add(note);
			}
		}
		return ret;
	}

	public NoteModel getNote(String guid) {
		if (isDirty) {
			updateAllNotes();
		}
		return allNotes.get(guid);
	}
	
	public NoteModel getNoteByTitle(String title) {
		if (isDirty) {
			updateAllNotes();
		}
		
		for (Map.Entry<String, NoteModel> entry : allNotes.entrySet()) {
			NoteModel note = entry.getValue();
			if (title == note.getTitle()) {
				return note;
			}
		}
		return null;
	}
	
	public void updateAllNotes() {
		isDirty = false;
		allNotes.clear();
		List<NoteModel> noteList = noteMgr.getAll();
		if (noteList != null) {
			for (NoteModel note : noteList) {
				note.setContent(toDispHtml(note.getContent()));
				allNotes.put(note.getGuid(), note);
			}
		} else {
			Log.i(Global.AppName, "[NoteService::updateAllNotes] no note existed.");
		}
	}
	
	// Update a note's title
 	public void updateNoteTitle(String guid, String title) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteTitle");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setTitle(title);
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteTitle");
	}
	// Update a note's creation date
	public void updateNoteCreatedDate(String guid, Date date) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteCreatedDate");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setCreated(date.getTime());
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteCreatedDate");
	}
	// Update a note's creation date
	public void updateNoteAlteredDate(String guid, Date date) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteAlteredDate");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setUpdated(date.getTime());
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteAlteredDate");
	}
	// Update a note's creation date
	public void updateNoteSubjectDate(String guid, Date date) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteSubjectDate");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setAttributeSubjectDate(date.getTime());
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteSubjectDate");
	}
	// Update a note's creation date
	public void updateNoteAuthor(String guid, String author) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteSubject");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setAttributeAuthor(author);
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteSubject");
	}
	// Update a note's geo tags
	public void updateNoteGeoTags(String guid, Double lon, Double lat, Double alt) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteGeoTags");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setAttributeLongitude(lon);
			noteModel.setAttributeLatitude(lat);
			noteModel.setAttributeAltitude(alt);
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteGeoTags");
		
	}
	// Update a note's creation date
	public void updateNoteSourceUrl(String guid, String url) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteSourceUrl");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setAttributeSourceUrl(url);
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteSourceUrl");
	}
	// Update the notebook that a note is assigned to
	public void updateNoteNotebook(String guid, String notebookGuid, boolean expungeFromRemote) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteNotebook");
		
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel == null) {
			return ;
		}
		String currentNotebookGuid = noteModel.getNotebookGuid();
		
		// If we are going from a synchronized notebook to a local notebook, we
		// need to tell Evernote to purge the note online.  However, if this is  
		// conflicting change we move it to the local notebook without deleting it 
		// or it would then delete the copy on the remote server.
		
		noteModel.setNotebookGuid(notebookGuid);
		noteModel.setDirty(true);
		noteMgr.update(noteModel);

		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteNotebook");
	}
	// Update a note's title
	public void updateNoteContent(String guid, String content) {
		Log.i("NoteManagerService", "Entering NoteTable.updateNoteContent");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			// TODO
			//noteModel.setContent(fromDispHtml(content));
			noteModel.setContent(content);
			noteModel.setUpdated(new Date().getTime());
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
		Log.i("NoteManagerService", "Leaving NoteTable.updateNoteContent");
	}

	// Delete a note
	public void deleteNote(String guid) {
		Log.i("NoteManagerService", "Entering NoteTable.deleteNote");
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setDeleted(new Date().getTime());
			noteModel.setActive(false);
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
			isDirty = true;
		}
		Log.i("NoteManagerService", "Leaving NoteTable.deleteNote");
	}
	public void restoreNote(String guid) {
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setDeleted(0);
			noteModel.setActive(true);
			noteModel.setDirty(true);
			noteMgr.update(noteModel);
		}
	}
	// Purge a note (actually delete it instead of just marking it deleted)
	public void expungeNote(String guid, boolean permanentExpunge, boolean needsSync) {
		Log.i("NoteManagerService", "Entering NoteTable.expungeNote");
		
		if (!permanentExpunge) {
			hideExpungedNote(guid, needsSync);
			return;
		}
	
		noteMgr.remove(guid);

		NoteResourceManager noteResMgr = (NoteResourceManager)ApplicationContext.getSingleton().getBean("NoteResourceManager");
		noteResMgr.removeByNoteGuid(guid);
		
		NoteTagsManager noteTagsMgr = (NoteTagsManager)ApplicationContext.getSingleton().getBean("NoteTagsManager");
		noteTagsMgr.removeByNoteGuid(guid);
		
		Log.i("NoteManagerService", "Leaving NoteTable.expungeNote");
	}

	public void expungeNotesByNotebook(String notebookGuid, boolean permanentExpunge, boolean needsSync) {
		List<NoteModel> notes = noteMgr.findByNotebookGuid(notebookGuid);
		for (int i=0; i<notes.size(); i++) {
			expungeNote(notes.get(i).getGuid(), permanentExpunge, needsSync);
		}
	}

	// Purge a note (actually delete it instead of just marking it deleted)
	public void hideExpungedNote(String guid, boolean needsSync) {        
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setExpunged(true);
			noteMgr.update(noteModel);
		}
		
		NoteResourceManager noteResMgr = (NoteResourceManager)ApplicationContext.getSingleton().getBean("NoteResourceManager");
		noteResMgr.removeByNoteGuid(guid);
		
		NoteTagsManager noteTagsMgr = (NoteTagsManager)ApplicationContext.getSingleton().getBean("NoteTagsManager");
		noteTagsMgr.removeByNoteGuid(guid);
		
	}

	// Get a list of notes that need to be updated
	public boolean isNoteDirty(String guid) {
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel == null) {
			return false;
		}
		
		return noteModel.isDirty();
	}

	// Reset the dirty bit
	public void  resetDirtyFlag(String guid) {
		NoteModel noteModel = noteMgr.get(guid);
		if (noteModel != null) {
			noteModel.setDirty(false);
			noteMgr.update(noteModel);
		}
	}
	
	// Get all notes
	public List<String> getAllGuids() {
		List<String> notes = new ArrayList<String>();
		
		List<NoteModel> noteModelList = noteMgr.getAll();
		for (int i=0; i<noteModelList.size(); ++i) {
			notes.add(noteModelList.get(i).getGuid()); 
		}
		return notes;
	}

	// Get all notes

	public int getAllNoteCount() {
		if (isDirty) {
			updateAllNotes();
		}
		
		return allNotes.size();
	}
	
	public int getAllActiveNoteCount() {
		if (isDirty) {
			updateAllNotes();
		}
		
		int ret = 0;
		for (Map.Entry<String, NoteModel> entry : allNotes.entrySet()) {
			NoteModel note = entry.getValue();
			if (note.isActive()) {
				++ret;
			}
		}

		return ret;
	}
	
	public int getActiveAddressNoteCount() {
		if (isDirty) {
			updateAllNotes();
		}
		
		int ret = 0;
		for (Map.Entry<String, NoteModel> entry : allNotes.entrySet()) {
			NoteModel note = entry.getValue();
			if (note.isActive()) {
				if (note.getAttributeLatitude() != 0 || note.getAttributeLongitude() != 0) {
					++ret;
				}
			}
		}

		return ret;
		
	}
	
	public List<NoteModel> getActiveAddressNotes() {
		if (isDirty) {
			updateAllNotes();
		}
		
		List<NoteModel> ret = new ArrayList<NoteModel>();
		for (Map.Entry<String, NoteModel> entry : allNotes.entrySet()) {
			NoteModel note = entry.getValue();
			if (note.isActive()) {
				if (note.getAttributeLatitude() != 0 || note.getAttributeLongitude() != 0) {
					ret.add(note);
				}
			}
		}
		return ret;
	}

	
	public int getNotebookNoteCount(String notebookGuid) {
		return getNotebookNotes(notebookGuid).size();
	}
	
	public int getNotebookActiveNoteCount(String notebookGuid) {
		return getNotebookActiveNotes(notebookGuid).size();
	}
	
	public int getTagNoteCount(String tagGuid) {
		return getTagNotes(tagGuid).size();
	}
	
	public int getTagActiveNoteCount(String tagGuid) {
		return getTagActiveNotes(tagGuid).size();
	}
	
	public int getAddressNoteCount() {
		return 0;
	}
	
	// Fix CRLF problem that is on some notes
	private String fixCarriageReturn(String note) {
		if (note == null || !Global.enableCarriageReturnFix)
			return note;
		String a0 = Integer.valueOf("a0", 16).toString();
		note = note.replace("<div>"+a0+"</div>", "<div>&nbsp;</div>");
		return note.replace("<div/>", "<div>&nbsp;</div>");
	}
	
	private String fromDispHtml(String html) {
		NoteResourceService nrs = (NoteResourceService)ApplicationContext.getSingleton().getBean("NoteResourceService");
		// res
    	Document document = Jsoup.parse(html);
    	Elements images = document.body().getElementsByTag("img");
    	ListIterator<Element> it = images.listIterator();
    	while (it.hasNext()) {
    		Element elem = it.next();
    		String filename = elem.attr("src");
    		String guid = Global.getFileNameNoEx(filename);
    		elem.removeAttr("src");
    		elem.attr("guid", guid);
    		NoteResourceModel nrm = nrs.getNoteResource(guid);
    		if (nrm != null) {
    			elem.tagName("en-media");
    			String ext = Global.getFileExName(nrm.getAttributeFileName());
    			if (Global.getResFileType(nrm.getAttributeFileName()) == Global.RES_TYPE_IMAGE) { 
    				elem.attr("type", "image/" + ext);
    			} else {
    				elem.attr("type", "application/" + ext);
    			}
    		}
    	}
    	
    	Elements sounds = document.body().getElementsByTag("div");
    	ListIterator<Element> soundIt = sounds.listIterator();
    	while (soundIt.hasNext()) {
    		Element elem = soundIt.next();
    		String background = elem.attr("style");
    		if (StringUtils.isNull(background) || !background.equals("background:url('sound.png') no-repeat;width:100px;height:50px")) {
    			continue;
    		}
    		
    		String soundDisp = elem.val();
    		String guid = Global.getSoundGuid(soundDisp);
    		if (StringUtils.isNull(guid)) {
    			continue;
    		}
    		
    		elem.removeAttr("src");
    		elem.attr("guid", guid);
    		NoteResourceModel nrm = nrs.getNoteResource(guid);
    		if (nrm != null) {
    			elem.tagName("en-media");
    			String ext = Global.getFileExName(nrm.getAttributeFileName());
    			if (Global.getResFileType(nrm.getAttributeFileName()) == Global.RES_TYPE_IMAGE) { 
    				elem.attr("type", "image/" + ext);
    			} else {
    				elem.attr("type", "application/" + ext);
    			}
    		}
    	}

    	return document.html();
	}
	
	private String toDispHtml(String dbString) {
		NoteResourceService nrs = (NoteResourceService)ApplicationContext.getSingleton().getBean("NoteResourceService");
		// res
    	Document document = Jsoup.parse(dbString);
    	Elements images = document.body().getElementsByTag("en-media");
    	ListIterator<Element> it = images.listIterator();
    	while (it.hasNext()) {
    		Element elem = it.next();
    		String guid = elem.attr("guid");
    		String type = elem.attr("type");
    		String ext = Global.getExtFromType(type);
    		String filename = guid + "." + ext;
//    		if (Global.getResFileType(filename) == Global.RES_TYPE_SOUND) {
//    			NoteResourceModel nrm = nrs.getNoteResource(Global.getFileNameNoEx(filename));
//    			if (nrm != null) {
//    				
//    				String soundDisp = "yinote "+Global.getDate(nrm.attributeTimestamp);
//    				Global.addSound(soundDisp, guid);
//    				String html = "<div style=\"background:url('sound.png') no-repeat;width:100px;height:50px\">" + soundDisp + "</div>";
//    				elem.append(html);
//    				elem.remove();
//    			}
//    		} else {
	    		elem.tagName("img");
	    		elem.attr("src", filename);
	    		elem.removeAttr("type");
	    		elem.removeAttr("guid");
//    		}
    	}
    	
    	return document.html();
		
	}
	
	public JSONObject toJsonData(NoteModel note) {
		JSONObject noteParam = new JSONObject();
		try {
			noteParam.put("id", note.getGuid());
			noteParam.put("author", note.getAttributeAuthor());
			noteParam.put("title", note.getTitle());
			noteParam.put("content", note.getContent());
			noteParam.put("createdTime", 0);
			noteParam.put("updatedTime", 0);
			noteParam.put("updateSequenceNumber", note.getUpdateSequenceNumber());
			noteParam.put("notebookId", note.getNotebookGuid());
			JSONObject attrParam = new JSONObject();
			attrParam.put("subjectDate", note.getAttributeSubjectDate());
			noteParam.put("attribute", attrParam);
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

		return noteParam;
	}
	
	public NoteModel fromJsonData(JSONObject noteParam) {
		NoteModel note = new NoteModel();
		try {
			note.setGuid(noteParam.getString("id"));
			note.setAttributeAuthor(noteParam.getString("author"));
			note.setTitle(noteParam.getString("title"));
			note.setContent(noteParam.getString("content"));
			note.setCreated(noteParam.getLong("createdTime"));
			note.setUpdated(noteParam.getLong("updatedTime"));
			note.setUpdateSequenceNumber(noteParam.getInt("updateSequenceNumber"));
			note.setNotebookGuid(noteParam.getString("notebookId"));
			
			JSONObject attrParam = noteParam.getJSONObject("attribute");
			if (attrParam != null) {
				note.setAttributeSubjectDate(attrParam.getLong("subjectDate"));
			}
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		return note;
	}

}
